package hikari.gui.assets;

import com.gooddies.ballontips.ToolTipHandler;
import com.gooddies.wiring.Wiring;
import com.gooddies.wiring.annotations.PostWiring;
import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.gui.guicomponents.new_draggable_tree.hDraggableTree;
import hikari.managers.ScriptManager;
import hikari.preferences.GameProperties;
import hikari.script.ScriptRegisterProcessor;
import hikari.utils.Utils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreePath;

/**
 * @author sad
 */
@WiringComponent(singleton = true)
public class AssetsTree extends hDraggableTree {

    @Wire
    private ScriptManager scriptManager;
    @Wire
    private GameProperties gameProperties;
    @Wire
    private AssetsTreeRenderer assetsTreeRenderer;
    @Wire
    private Utils utils;
    public static String[] allowed3dModelExtensions = new String[]{"x", "b3d", "fbx", "obj", "ms3d", "md3", "md2", "3ds", "mesh", "lwo"};
    public static String[] allowedTextureExtensions = new String[]{"jpg", "png", "tga", "bmp", "pcx", "psd", "wal"};
    public static String[] allowedScriptExtensions = new String[]{"cpp", "h"};
    public static String[] allowedTemplateExtensions = new String[]{"tpl"};

    private AssetsTree() {
        super("Root");
        setRootVisible(true);
        setShowsRootHandles(true);

        ((DefaultMutableTreeNode) getModel().getRoot()).add(new DefaultMutableTreeNode());
        addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                e.getPath().getLastPathComponent();
            }
        });

        addTreeWillExpandListener(new TreeWillExpandListener() {
            @Override
            public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
                TreePath path = event.getPath();
                populateNode(path, (DefaultMutableTreeNode) path.getLastPathComponent(), getDefaultModel());
            }

            @Override
            public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
            }
        });

        addPopupMenu();
        addMouseHandler();
    }

    @PostWiring
    private void init() {
        setCellRenderer(assetsTreeRenderer);
    }

    private void addPopupMenu() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    JPopupMenu menu = new JPopupMenu();
                    JMenuItem createFolder = new JMenuItem("Create folder");
                    createFolder.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            createFolder();
                        }
                    });

                    menu.add(createFolder);
                    JMenuItem createScript = new JMenuItem("Create script");
                    createScript.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            createScript();
                        }
                    });

                    menu.add(createScript);
                    JMenuItem refreshMenu = new JMenuItem("Refresh");
                    refreshMenu.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            refreshTree();
                        }
                    });

                    menu.add(refreshMenu);
                    menu.show(AssetsTree.this, e.getX(), e.getY());
                }
            }
        });
    }

    private void addMouseHandler() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    ifDoubleClick();
                }
            }
        });
    }

    private void ifDoubleClick() {
        TreePath path = getSelectionPath();
        if (path == null) {
            return;
        }

        if (isTreePathScript(path)) {
            String filePath = treePathToFilePath(path);
            onScriptOpen(filePath);
        }
    }

    private void onScriptOpen(String path) {
        utils.start(path);
    }

    private TreePath getRootPath() {
        return new TreePath((DefaultMutableTreeNode) getDefaultModel().getRoot());
    }

    public void refreshTree() {
        refreshPath(getRootPath());
    }

    private void createFolder() {
        TreePath path = getSelectionPath();
        if (path == null || !isTreePathFolder(path)) {
            errorTooltip("Please select a folder");
            return;
        }
        String dirPath = treePathToFilePath(path);
        File directory = new File(dirPath);
        String dirName = utils.prompt(this, "Please enter new directory name", "Directory should contains only english characters, digits and _", "a-zA-Z0-9_", "Directory name contains wrong characters");
        if (dirName == null) {
            return;
        }
        String newPath = directory.getAbsolutePath() + "/" + dirName;
        File newDir = new File(newPath);
        if (newDir.exists()) {
            errorTooltip("Directory with name " + dirName + " already exists");
            return;
        }

        newDir.mkdir();
        refreshPath(path);
    }

    private void createScript() {
        TreePath path = getSelectionPath();
        if (path == null || !isTreePathFolder(path)) {
            errorTooltip("Please select a folder");
            return;
        }

        String dirPath = treePathToFilePath(path);
        String scriptName = utils.prompt(this, "Please enter new script name", "Script should contains only english characters, digits and _", "a-zA-Z0-9_", "Script name contains wrong characters");
        if (scriptName == null) {
            return;
        }
        if (!scriptManager.createNewScript(dirPath, scriptName)) {
            errorTooltip("cannot create script file " + scriptName);
            return;
        }

        refreshPath(path);
        ScriptRegisterProcessor processor = Wiring.getComponent(ScriptRegisterProcessor.class);
        processor.process();
    }

    private String getRootDirectory() {
        return gameProperties.getMediaFolder();
    }

    public void refreshPath(TreePath path) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        populateNode(path, (DefaultMutableTreeNode) path.getLastPathComponent(), getDefaultModel());
        getDefaultModel().nodeChanged(node);
        getDefaultModel().nodeStructureChanged(node);
    }

    public void errorTooltip(String message) {
        ToolTipHandler.error((JComponent) this.getParent(), message);
    }

    public String treePathToFilePath(TreePath treePath) {
        StringBuilder sb = new StringBuilder();
        sb.append(utils.endWithFileSeparator(getRootDirectory()));

        Object[] pathChunks = treePath.getPath();
        for (int i = 1; i < pathChunks.length; i++) {
            if (i != 1) {
                sb.append(File.separator);
            }

            String currentDir = pathChunks[i].toString();
            sb.append(currentDir);
        }

        return sb.toString();
    }

    public String getRelativeAssetsPath(String path) {
        if (path == null) {
            return null;
        }

        return utils.makePathRelative(path, getRootDirectory());
    }

    public String getAbsoluteAssetPathFromRelativePath(String relativePath) {
        return utils.combinePaths(getRootDirectory(), relativePath);
    }

    public boolean isFile3dModel(File file) {
        String fileExtension = utils.getFileExtension(file.getAbsolutePath()).toLowerCase();
        return isExtension3dModel(fileExtension);
    }

    public boolean isFileScript(File file) {
        String fileExtension = utils.getFileExtension(file.getAbsolutePath()).toLowerCase();
        return isExtensionScript(fileExtension);
    }

    public boolean isFileTemplate(File file) {
        String fileExtension = utils.getFileExtension(file.getAbsolutePath()).toLowerCase();
        return isExtensionTemplate(fileExtension);
    }

    public boolean isFileTexture(File file) {
        String fileExtension = utils.getFileExtension(file.getAbsolutePath()).toLowerCase();
        return isExtensionTexture(fileExtension);
    }

    public boolean isExtension3dModel(String ext) {
        for (String allowedExtension : allowed3dModelExtensions) {
            if (allowedExtension.equals(ext)) {
                return true;
            }
        }

        return false;
    }

    public boolean isExtensionScript(String ext) {
        for (String allowedExtension : allowedScriptExtensions) {
            if (allowedExtension.equals(ext)) {
                return true;
            }
        }

        return false;
    }

    public boolean isExtensionTemplate(String ext) {
        for (String allowedExtension : allowedTemplateExtensions) {
            if (allowedExtension.equals(ext)) {
                return true;
            }
        }

        return false;
    }

    public boolean isExtensionTexture(String ext) {
        for (String allowedExtension : allowedTextureExtensions) {
            if (allowedExtension.equals(ext)) {
                return true;
            }
        }

        return false;
    }

    public boolean isTreePathFolder(TreePath path) {
        String filePath = treePathToFilePath(path);
        return filePath != null && new File(filePath).isDirectory();
    }

    @SuppressWarnings("UnusedDeclaration")
    public boolean isTreePathTexture(TreePath path) {
        String filePath = treePathToFilePath(path);
        if (filePath == null) {
            return false;
        }

        return isFileTexture(new File(filePath));
    }

    public boolean isTreePathScript(TreePath path) {
        String filePath = treePathToFilePath(path);
        if (filePath == null) {
            return false;
        }

        return isFileScript(new File(filePath));
    }

    public boolean isTreePathTemplate(TreePath path) {
        String filePath = treePathToFilePath(path);
        if (filePath == null) {
            return false;
        }

        return isFileTemplate(new File(filePath));
    }

    public boolean isTreePath3dModel(TreePath path) {
        String filePath = treePathToFilePath(path);
        if (filePath == null) {
            return false;
        }

        return isFile3dModel(new File(filePath));
    }

    public boolean shouldShowFile(File file) {
        if (file.isDirectory()) {
            return true;
        }

        if (file.isHidden()) {
            return false;
        }

        if (isFile3dModel(file) || isFileTexture(file) || isFileScript(file) || isFileTemplate(file)) {
            return true;
        }

        return false;
    }

    private void sortFiles(File[] files) {
        Arrays.sort(files, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (o1.isDirectory() && o2.isDirectory()) {
                    return o1.getName().compareTo(o1.getName());
                }

                if (o1.isDirectory()) {
                    return -1;
                }

                if (o2.isDirectory()) {
                    return 1;
                }

                return o1.getName().compareToIgnoreCase(o2.getName());
            }
        });
    }

    public void populateNode(TreePath path, DefaultMutableTreeNode node, DefaultTreeModel treeModel) {
        String filePath = treePathToFilePath(path);
        node.removeAllChildren();
        File file = new File(filePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            sortFiles(files);
            for (File childFile : files) {
                if (shouldShowFile(childFile)) {
                    DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(childFile.getName());
                    if (childFile.isDirectory()) {
                        newNode.add(new DefaultMutableTreeNode("dummy"));
                        newNode.setAllowsChildren(true);
                    } else {
                        newNode.setAllowsChildren(false);
                    }

                    treeModel.insertNodeInto(newNode, node, node.getChildCount());
                }
            }
        }

        treeModel.nodeChanged(node);
        treeModel.nodeStructureChanged(node);
    }
}
